"use strict";

const inquirer = require("inquirer");
const fse = require("fs-extra");
const semver = require("semver");
const log = require("@spiritos-cli/log");
const Command = require("@spiritos-cli/command");
const getTemplate = require("./templateApi");
const glob = require("glob");
const ejs = require("ejs");
const {
  spinnerStart,
  sleep,
  execAsync,
  checkCommand,
  testStringName,
} = require("@spiritos-cli/utils");

const TYPE_PROJECT = "project";
const TYPE_COMPONENT = "component";

const TEMPLATE_TYPE_CUSTOM = "custom";
const TEMPLATE_TYPE_NORMAL = "normal";

const WHITE_COMMAND = ["npm", "cnpm"];

class InitCommand extends Command {
  init() {
    this.projectName = this._argv[0] || ""; //项目名称
    this.force = !!this._cmd._optionValues.force; //是否强制执行
    log.verbose("projectName", this.projectName);
    log.verbose("force", this.force);
  }

  async exec() {
    try {
      // 1. 准备阶段
      const projectInfo = await this.prepare();
      if (projectInfo) {
        // 2. 下载模板
        //log.verbose("projectInfo", projectInfo);
        this.projectInfo = projectInfo;
        await this.downloadTemplate();
        //3. 安装模板
        await this.installTemplate();
      }
    } catch (e) {
      log.error(e.message);
      if (process.env.LOG_LEVEL === "verbose") {
        console.log(e);
      }
    }
  }

  /**
   * @description 下载模板
   *  1.通过项目模板API获取项目模板信息
   *  1.1通过egg.js搭建一套后端系统
   *  1.2通过npm存储项目模板（vue-cli/vue-element-admin）
   *  1.3将项目模板信息存储到mongodb数据库中
   *  1.4通过egg.js获取Mongodb中的数据并且通过API返回
   */
  async downloadTemplate() {
    const Package = require("@spiritos-cli/package");
    const userHome = require("user-home");
    const { projectTemplate } = this.projectInfo;
    this.templateInfo = this.template.find(
      (item) => item.nmpName === projectTemplate
    );
    const { nmpName, version } = this.templateInfo;
    const targetPath = path.resolve(userHome, ".spiritos-cli", "template");
    const storeDir = path.resolve(
      userHome,
      ".spiritos-cli",
      "template",
      "node_modules"
    );
    const templateNpm = new Package({
      targetPath,
      storeDir,
      packageName: nmpName,
      packageVersion: version,
    });
    const isExists = await templateNpm.exists(); //判断是否下载过
    if (isExists) {
      const spinner = spinnerStart("正在更新模板……");
      await sleep();
      try {
        await templateNpm.update();
      } catch (error) {
        throw error;
      } finally {
        spinner.stop(true);
        if (await templateNpm.exists()) {
          log.warn("更新成功");
          this.templateNpm = templateNpm;
        }
      }
    } else {
      const spinner = spinnerStart("正在下载模板……");
      await sleep();
      try {
        await templateNpm.install();
      } catch (error) {
        throw error;
      } finally {
        spinner.stop(true);
        if (await templateNpm.exists()) {
          log.warn("下载成功");
          this.templateNpm = templateNpm;
        }
      }
    }
  }

  /**
   * 初始化准备方法
   */
  async prepare() {
    // 0判断是否有模板
    const template = await getTemplate();
    if (!template || template.length === 0) {
      throw new Error("模板不存在");
    }
    this.template = template;
    // 1. 判断当前目录是否为空
    const localPath = process.cwd();
    if (!this.isDirEmpty(localPath)) {
      let ifContinue = false;
      if (!this.force) {
        // 询问是否继续创建
        ifContinue = (
          await inquirer.prompt({
            type: "confirm",
            name: "ifContinue",
            default: false,
            message: "当前文件夹不为空，是否继续创建项目？",
          })
        ).ifContinue;
        if (!ifContinue) {
          return;
        }
      }
      // 2. 是否启动强制更新
      if (ifContinue || this.force) {
        // 给用户做二次确认
        const { confirmDelete } = await inquirer.prompt({
          type: "confirm",
          name: "confirmDelete",
          default: false,
          message: "是否确认清空当前目录下的文件？",
        });
        if (confirmDelete) {
          // 清空当前目录
          fse.emptyDirSync(localPath);
        }
      }
    }
    return this.getProjectInfo();
  }

  /**
   * 命令行交互
   */
  async getProjectInfo() {
    let projectInfo = {};
    // 1. 选择创建项目或组件
    const { type } = await inquirer.prompt({
      type: "list",
      name: "type",
      message: "请选择初始化类型",
      default: TYPE_PROJECT,
      choices: [
        {
          name: "项目",
          value: TYPE_PROJECT,
        },
        {
          name: "组件",
          value: TYPE_COMPONENT,
        },
      ],
    });
    log.verbose("type", type);
    this.template = this.template.filter((item) => item.tag.includes(type));
    const title = type === TYPE_PROJECT ? "项目" : "模板";
    //判断是否已经有项目名称
    const projectNamePromt = {
      type: "input",
      name: "projectName",
      message: `请输入${title}名称`,
      default: "",
      validate: function (v) {
        // 用户输入失败的时候给提示
        const done = this.async();
        setTimeout(function () {
          if (!testStringName(v)) {
            done(`请输入合法的${title}名称`);
            return;
          }
          done(null, true);
        }, 0);
      },
      filter: function (v) {
        return v;
      },
    };
    //项目名称合法
    const projectPromtArr = [];
    if (testStringName(this.projectName)) {
      projectInfo.projectName = this.projectName;
    } else {
      projectPromtArr.push(projectNamePromt);
    }
    projectPromtArr.push(
      {
        type: "input",
        name: "projectVersion",
        message: "请输入版本号",
        default: "",
        validate: function (v) {
          const done = this.async();
          setTimeout(function () {
            if (!!!semver.valid(v)) {
              done("请输入合法的版本号");
              return;
            }
            done(null, true);
          }, 0);
        },
        filter: function (v) {
          if (!!semver.valid(v)) {
            return semver.valid(v);
          } else {
            return v;
          }
        },
      },
      {
        type: "list",
        message: `请选择${title}模板`,
        name: "projectTemplate",
        choices: this.createTemplateChioces(),
      }
    );
    //根据不同的选择类型过滤内容
   
    if (type === TYPE_PROJECT) {
      let project = await inquirer.prompt(projectPromtArr);
      projectInfo = {
        ...projectInfo,
        type,
        ...project,
      };
    } else if (type === TYPE_COMPONENT) {
      const projectDes = {
        type: "input",
        name: "componentDescription",
        message: `请输入模板描述`,
        default: "",
        validate: function (v) {
          // 用户输入失败的时候给提示
          const done = this.async();
          setTimeout(function () {
            if (!v) {
              done(`请输入模板描述`);
              return;
            }
            done(null, true);
          }, 0);
        },
      };
      projectPromtArr.push(projectDes);
      let component = await inquirer.prompt(projectPromtArr);
      projectInfo = {
        ...projectInfo,
        type,
        ...component,
      };
    }
    //将驼峰形式的字符串转换成className
    if (projectInfo.projectName) {
      projectInfo.name = projectInfo.projectName;
      projectInfo.className = require("kebab-case")(
        projectInfo.projectName
      ).replace(/^-/, "");
    }
    if (projectInfo.projectVersion) {
      projectInfo.version = projectInfo.projectVersion;
    }
    if (projectInfo.componentDescription) {
      projectInfo.description = projectInfo.componentDescription;
    }
    return projectInfo;
  }

  /**
   * 清空文件
   * @param {string} localPath 文件路径
   */
  isDirEmpty(localPath) {
    let fileList = fs.readdirSync(localPath);
    // 文件过滤的逻辑
    fileList = fileList.filter(
      (file) => !file.startsWith(".") && ["node_modules"].indexOf(file) < 0
    );
    return !fileList || fileList.length <= 0;
  }

  /**
   * 创建模板的选择内容方法
   */
  createTemplateChioces() {
    return this.template.map((item) => ({
      value: item.nmpName,
      name: item.name,
    }));
  }

  /**
   * 安装模板
   */
  async installTemplate() {
    if (this.templateInfo) {
      if (!this.templateInfo.type) {
        this.templateInfo.type = TEMPLATE_TYPE_NORMAL;
      }
      if (this.templateInfo.type === TEMPLATE_TYPE_NORMAL) {
        //标准模板
        await this.installNormalTemplate();
      } else if (this.templateInfo.type === TEMPLATE_TYPE_CUSTOM) {
        //自定义模板
        await this.installCustomTemplate();
      } else {
        throw new Error("无法识别模板类型！");
      }
    } else {
      throw new Error("模板信息不存在！");
    }
  }

  async execCommand(command, message) {
    let ret;
    if (command) {
      const arrCmd = command.split(" ");
      const cmd = checkCommand(WHITE_COMMAND, arrCmd[0]);
      if (!cmd) {
        throw new Error("无效命令" + command);
      }
      const args = arrCmd.slice(1);
      ret = await execAsync(cmd, args, {
        cwd: process.cwd(),
        stdio: "inherit",
      });
    }
    if (ret !== 0) {
      throw new Error(message);
    }
    return ret;
  }

  /**
   * ejs渲染文件
   * @param {object} options  忽略的文件对象
   */
  async ejsRender(options) {
    const dir = process.cwd();
    const projectInfo = this.projectInfo;
    return new Promise((resolve, reject) => {
      glob(
        "**",
        {
          cwd: dir,
          ignore: options.ignore || "",
          nodir: true, //只要具体文件
        },
        (err, files) => {
          if (err) {
            reject(err);
          }
          //获取所有文件渲染完成后执行方法
          Promise.all(
            files.map((file) => {
              //正式渲染文件
              const filePath = path.join(dir, file);
              return new Promise((resolve1, reject1) => {
                ejs.renderFile(filePath, projectInfo, {}, (err, result) => {
                  if (err) {
                    reject1(err);
                  } else {
                    fse.writeFileSync(filePath, result); //写入文件
                    resolve1(result);
                  }
                });
              });
            })
          )
            .then(() => {
              resolve();
            })
            .catch(() => {
              reject();
            });
        }
      );
    });
  }

  /**
   * 安装标准模板到当前目录
   */
  async installNormalTemplate() {
    const spinner = spinnerStart("正在安装模板");
    await sleep();
    try {
      const templatePath = path.resolve(
        this.templateNpm.cacheFilePath,
        "template"
      );
      const targetPath = process.cwd();
      fse.ensureDirSync(templatePath);
      fse.ensureDirSync(targetPath);
      fse.copySync(templatePath, targetPath);
    } catch (e) {
      throw e;
    } finally {
      spinner.stop(true);
      log.warn("安装成功");
    }
    //const templateIgnore = this.templateInfo.ignore || [];
    const ignore = ["node_modules/**", "public/**",'**.png']; //忽略的文件
    await this.ejsRender({ ignore });
    //安装依赖，执行启动命令
    const { installCommand, startCommand } = this.templateInfo;
    await this.execCommand(installCommand, "安装过程中失败！");
    await this.execCommand(startCommand, "运行过程失败！");
  }

  async installCustomTemplate() {
    console.log("安装自定义模板");
  }
}

function init(argv) {
  log.verbose("argv", argv);
  return new InitCommand(argv);
}

module.exports = init;
module.exports.InitCommand = InitCommand;
